AWS LambdaのPython 3.8ランタイムで不足する共有ライブラリと格闘しながらMediaInfoを動かしてみた
はじめに
清水です。先日、AWS Lambda(Python)から動画やオーディオファイルの各種情報を取得できるツールMediaInfoを使い、Lambda関数内で動画ファイルのメタ情報を取得してみました。以下のエントリにまとめています。
上記エントリではLambdaのランタイムはPython 3.7
を選択していました。しかし2020年3月現在、サポートされているLambdaランタイムのうちPythonの最新版はPython 3.8
となります。本エントリではこのPython 3.8
のランタイムでMediaInfoを動作させてみた記録を、Python 3.7
ランタイムとの違いを中心にまとめてみます。具体的には、Python 3.8ではオペレーティングシステムがAmazon Linux 2となりますので該当環境でのMediaInfoのコンパイル作業を行いました。(Python 3.7ではオペレーティングシステムはAmazon Linux (いわゆる1のほう)でした。)またPython 3.7ランタイムの環境ではMediaInfoの実行ファイルのみをLayerに含めればよかったのですが、Python 3.8ランタイムの環境ではこのままだとMediaInfo実行時に共有ライブラリ関連のファイルが見つからずエラーとなってしまいます。不足する共有ライブラリを確認し、これらもMediaInfoの実行ファイルと一緒にLayerとしてLambda関数に設定を行いました。
Amazon Linux 2でのMediaInfo実行ファイルのコンパイル
まずはLambda関数内で使用するMediaInfoの実行ファイルを準備します。以前のPython 3.7で動作させたときは、オペレーティングシステムがAmazon Linux(Amazon Linux 2ではなく、1のほう)でした。AWS Lambda 開発者ガイド「AWS Lambda ランタイム」の項目を確認してみると、Python 3.8ランタイムではオペレーティングシステムはAmazon Linux 2とのことです。イメージについては「カスタム」という表記のみでした *1ので、東京リージョン(ap-northeast-1)で利用できる現段階で最新のAMIを利用しました。
- AMI Name
- amzn2-ami-hvm-2.0.20200304.0-x86_64-gp2
- AMI ID
- ami-052652af12b58691f
- Description
- Amazon Linux 2 AMI 2.0.20200304.0 x86_64 HVM gp2
コンパイルはこちらの手順そのままで行うことができました。つまり、Amazon Linux(Python 3.7ランタイム想定)のときと変わりはありません。詳細は以前のPython 3.7のエントリを参照ください。概要のみまとめます。また今回EC2のインスタンスタイプはt2.mediumを使用しました。
'Development Tools'
とlibcurl-devel
をyumでインストールします。(前者はgroupinstallします。)
$ sudo yum groupinstall 'Development tools' $ sudo yum install libcurl-devel
MediaInfoのソースファイルをダウンロードして付属のスクリプトでコンパイルを行います。MediaInfoのバージョンは19.09です。
$ wget https://mediaarea.net/download/binary/mediainfo/19.09/MediaInfo_CLI_19.09_GNU_FromSource.tar.xz $ tar xvf MediaInfo_CLI_19.09_GNU_FromSource.tar.xz $ cd MediaInfo_CLI_GNU_FromSource $ ./CLI_Compile.sh --with-libcurl 2>&1 | tee CLI_Compile.log
コンパイルが完了すると以下のメッセージが表示されます。
MediaInfo (CLI) compiled MediaInfo executable is MediaInfo/Project/GNU/CLI/mediainfo For installing, cd MediaInfo/Project/GNU/CLI && make install
MediaInfo実行ファイルのみをLambda Layerに設定して動作確認
LambdaのLayer機能を使うため、MediaInfoの実行ファイルをzipにまとめます。
$ mkdir bin $ cp -p MediaInfo_CLI_GNU_FromSource/MediaInfo/Project/GNU/CLI/mediainfo ./bin/ $ zip mediainfo_v19.09.zip ./bin/*
zipファイルを展開すると以下のような構成になっています。
$ unzip mediainfo_v19.09.zip Archive: mediainfo_v19.09.zip inflating: bin/mediainfo $ ls -R .: bin mediainfo_v19.09.zip ./bin: mediainfo
この、Amazon Linux 2環境でコンパイルしたMediaInfoの実行ファイルのみをLayerに含んだ状態で動作するか、確認してみます。以前のエントリとは実行ファイルをコンパイルしたオペレーティングシステム環境、ランタイムのみが異なる状態ですね。手順詳細は以前のエントリをご参照ください。AWS CLIコマンドのみまとめていきます。
まずはLayerを作成します。Layer名はMediaInfo-Python38
としました。
$ aws lambda publish-layer-version \ --layer-name MediaInfo-Python38 \ --description "MediaInfo v19.09 (Amazon Linux 2)" \ --zip-file fileb://mediainfo_v19.09.zip \ --compatible-runtimes python3.8
続いてLambda関数を作成します。Lambda関数に設定するPythonのコードも以前のエントリと変わりはありません。(本エントリ最後にもまとめておきます。)コードはlambda_function.py
というファイルにまとめ、MediaInfo-on-lambda.zip
としてzipファイルにまとめている状態です。
$ zip MediaInfo-on-Lambda.zip lambda_function.py adding: lambda_function.py (deflated 63%)
$ aws lambda create-function \ --function-name MediaInfo-on-Lambda-Python38 \ --runtime python3.8 \ --role arn:aws:iam::123456789012:role/MediaInfo-on-Lambda-Role \ --handler lambda_function.lambda_handler \ --zip-file fileb://MediaInfo-on-Lambda.zip \ --layers arn:aws:lambda:ap-northeast-1:123456789012:layer:MediaInfo-Python38:1 \ --memory-size 256 \ --timeout 60
S3からLambdaをトリガするように設定を行います。まずはLambda側にInvokeFunctionの実行を許可します。
$ aws lambda add-permission \ --function-name MediaInfo-on-Lambda-Python38 \ --statement-id "s3-put-event" \ --action "lambda:InvokeFunction" \ --principal "s3.amazonaws.com" \ --source-arn "arn:aws:s3:::my-s3-bucket"
そしてS3バケット側に設定を行います。
$ aws s3api put-bucket-notification-configuration \ --bucket my-s3-bucket \ --notification-configuration '{"LambdaFunctionConfigurations": [{"LambdaFunctionArn": "arn:aws:lambda:ap-northeast-1:123456789012:function:MediaInfo-on-Lambda-Python38", "Id": "Execute-MediaInfo-on-Lambda-Python38", "Events": ["s3:ObjectCreated:*"]}]}'
以上の状態、繰り返しになりますがつまり、Python3.7ランタイムと比べて (1) MediaInfoの実行ファイルコンパイル環境をAmazon Linux 2に変更し、(2) LambdaのランタイムをPython3.8にした状態で動作を確認してみます。この変更だけですんなり動けば良いのですが……、実際にS3に動画ファイルをアップロードしLambda関数をキック、実行結果をCloudWatch Logsを確認すると以下のエラーが発生しており、MediaInfo自体が実行できていないことが確認できます。
mediainfo: error while loading shared libraries: libcurl.so.4: cannot open shared object file: No such file or directory
MediaInfo実行に不足しているライブラリと格闘
libcurl.so.4がNo such file or directory
実行エラーは共有ライブラリのlibcurl.so.4
が見つからず開けない、というものでした。確認したところ該当のファイルlibcurl.so.4
はMediaInfoをコンパイルしたAmazon Linux 2上の/lib64
ディレクトリ配下に存在しています。
$ ls -l /lib64/libcurl.so* lrwxrwxrwx 1 root root 16 3月 26 11:32 /lib64/libcurl.so -> libcurl.so.4.5.0 lrwxrwxrwx 1 root root 16 3月 7 03:42 /lib64/libcurl.so.4 -> libcurl.so.4.5.0 -rwxr-xr-x 1 root root 553840 10月 3 23:37 /lib64/libcurl.so.4.5.0
Lambda実行環境下ではこれが存在していない、ということのようですので、このlibcurl.so.4
についてもMediaInfo実行ファイルmediainfo
と一緒にLayerに設定してみます。lib
ディレクトリを作成してその中にlibcurl.so.4
のシンボリックリンク、ならびにその実態のlibcurl.so.4.5.0
を配置します。(/lib64
ディレクトリからのコピー時にはシンボリックリンクでコピーするよう、cp
コマンドに-a
オプションをつけておきます。)そしてMediaInfoの実行ファイルmediainfo
が格納されているbin
ディレクトリとlib
ディレクトリを一緒のzipファイルにまとめます。
$ mkdir lib $ cp -pa /lib64/libcurl.so.4* ./lib/ $ ls -l ./lib/ 合計 544 lrwxrwxrwx 1 ec2-user ec2-user 16 3月 7 03:42 libcurl.so.4 -> libcurl.so.4.5.0 -rwxr-xr-x 1 ec2-user ec2-user 553840 10月 3 23:37 libcurl.so.4.5.0 $ zip mediainfo_v19.09_libcurl.so.4.zip ./bin/* ./lib/* adding: bin/mediainfo (deflated 64%) adding: lib/libcurl.so.4 (deflated 52%) adding: lib/libcurl.so.4.5.0 (deflated 52%)
zipファイルを展開すると以下のような構成となっています。
$ unzip mediainfo_v19.09_libcurl.so.4.zip Archive: mediainfo_v19.09_libcurl.so.4.zip inflating: bin/mediainfo inflating: lib/libcurl.so.4 inflating: lib/libcurl.so.4.5.0 $ ls -R .: bin lib mediainfo_v19.09_libcurl.so.4.zip ./bin: mediainfo ./lib: libcurl.so.4 libcurl.so.4.5.0
作成したzipファイルで、Layerの新規バージョンを作成します。
$ aws lambda publish-layer-version \ --layer-name MediaInfo-Python38 \ --description "MediaInfo v19.09 (Amazon Linux 2) and libcurl.so.4" \ --zip-file fileb://mediainfo_v19.09_libcurl.so.4.zip \ --compatible-runtimes python3.8
Lambda関数で使用するLayerを変更します。
$ aws lambda update-function-configuration \ --function-name MediaInfo-on-Lambda-Python38 \ --layers arn:aws:lambda:ap-northeast-1:123456789012:layer:MediaInfo-Python38:2
このLayerにmediainfoとlibcurl.so.4を含んだ状態で、再度動作確認をしてみます。libcurl.so.4
のエラーは出なくなりましたが、またや共有ライブラリ不足のエラーが発生してしまいます。エラー内容は以下です。
mediainfo: error while loading shared libraries: libnghttp2.so.14: cannot open shared object file: No such file or directory
libnghttp2.so.14がNo such file or directory
共有ライブラリのlibnghttp2.so.14
が見つからずに開けない、ということで、こちらもlibcurl.so.4
のときと同様にLayerに設定してみましょう。libnghttp2.so.14
についてもMediaInfoをコンパイルしたAmazon Linux 2上の/lib64
ディレクトリに存在していました。
$ ls -l /lib64/libnghttp2.so* lrwxrwxrwx 1 root root 21 3月 7 03:42 /lib64/libnghttp2.so.14 -> libnghttp2.so.14.18.0 -rwxr-xr-x 1 root root 158280 10月 1 19:24 /lib64/libnghttp2.so.14.18.0
libcurl.so.4
を格納したlib
ディレクトリにlibnghttp2.so.14とその実体のlibnghttp2.so.14.18.0
をコピー、zipファイルに纏めていきます。
$ cp -pa /lib64/libnghttp2.so.14* ./lib/ $ ls -l ./lib/ 合計 700 lrwxrwxrwx 1 ec2-user ec2-user 16 3月 7 03:42 libcurl.so.4 -> libcurl.so.4.5.0 -rwxr-xr-x 1 ec2-user ec2-user 553840 10月 3 23:37 libcurl.so.4.5.0 lrwxrwxrwx 1 ec2-user ec2-user 21 3月 7 03:42 libnghttp2.so.14 -> libnghttp2.so.14.18.0 -rwxr-xr-x 1 ec2-user ec2-user 158280 10月 1 19:24 libnghttp2.so.14.18.0 $ zip mediainfo_libcurl_libnghttp2.zip ./bin/* ./lib/* adding: bin/mediainfo (deflated 64%) adding: lib/libcurl.so.4 (deflated 52%) adding: lib/libcurl.so.4.5.0 (deflated 52%) adding: lib/libnghttp2.so.14 (deflated 52%) adding: lib/libnghttp2.so.14.18.0 (deflated 52%)
このzipファイルでLayerの新規バージョンを作成しLambda関数で使用するよう設定します。
$ aws lambda publish-layer-version \ --layer-name MediaInfo-Python38 \ --description "MediaInfo v19.09 (Amazon Linux 2) and libcurl.so.4 and libngttp2.so.14" \ --zip-file fileb://mediainfo_libcurl_libnghttp2.zip \ --compatible-runtimes python3.8
$ aws lambda update-function-configuration \ --function-name MediaInfo-on-Lambda-Python38 \ --layers arn:aws:lambda:ap-northeast-1:123456789012:layer:MediaInfo-Python38:3
このLayerにmediainfoとlibcurl.so.4、さらにlibnghttp2.so.14を含んだ状態で、もういちど動作確認をしてみます。これでエラーが発生しなくなればよいのですが……、残念ながら、libnghttp2.so.14
のエラーは発生しなくなりますが、同様の共有ライブラリ不足のエラーが発生します。
mediainfo: error while loading shared libraries: libidn2.so.0: cannot open shared object file: No such file or directory
これまでのエラーと同様、共有ライブラリのlibidn2.so.0
が見つからずに開けない、ということです。それならlibcurl.so.4
、libnghttp2.so.14
のときと同じ手順で、/lib64
ディレクトリからコピーしてLayerに追加、という手順で対応できそうですね。ただこの共有ライブラリ不足、いつまで続くのでしょうか……。切りがないようにも思えます。実際このlibidn2.so.0
を追加しても、libssh2.so.1
、libldap-2.4.so.2
、liblber-2.4.so.2
と続いていきました。どこまで続くかわからないこともあり、エラーメッセージを逐一確認して対応していく、ではなく別のやり方を検討してみます。
MediaInfoの実行に必要な共有ライブラリファイルの洗い出し
Lambda上でのMediaInfo実行時に、どれほどの共有ライブラリが不足しているかわからないので、 MediaInfo実行に必要な共有ライブラリと、Lambda実行環境に存在している共有ライブラリのそれぞれを確認してみます。
MediaInfo実行時に必要な共有ライブラリ
MediaInfoの実行に必要な共有ライブラリについて、ldd
コマンドを使って確認します。MediaInfoをコンパイルしたAmazon Linux 2上で確認しました。実行結果は下記です。
[ec2-user@ip-10-82-11-17 ~]$ ldd ./bin/mediainfo linux-vdso.so.1 (0x00007ffd8dd56000) libz.so.1 => /lib64/libz.so.1 (0x00007f1e9da16000) libcurl.so.4 => /lib64/libcurl.so.4 (0x00007f1e9d791000) libpthread.so.0 => /lib64/libpthread.so.0 (0x00007f1e9d573000) libstdc++.so.6 => /lib64/libstdc++.so.6 (0x00007f1e9d1f1000) libm.so.6 => /lib64/libm.so.6 (0x00007f1e9ceb1000) libgcc_s.so.1 => /lib64/libgcc_s.so.1 (0x00007f1e9cc9b000) libc.so.6 => /lib64/libc.so.6 (0x00007f1e9c8f0000) libnghttp2.so.14 => /lib64/libnghttp2.so.14 (0x00007f1e9c6ca000) libidn2.so.0 => /lib64/libidn2.so.0 (0x00007f1e9c47b000) libssh2.so.1 => /lib64/libssh2.so.1 (0x00007f1e9c252000) libssl.so.10 => /lib64/libssl.so.10 (0x00007f1e9bfe3000) libcrypto.so.10 => /lib64/libcrypto.so.10 (0x00007f1e9bb8e000) libgssapi_krb5.so.2 => /lib64/libgssapi_krb5.so.2 (0x00007f1e9b942000) libkrb5.so.3 => /lib64/libkrb5.so.3 (0x00007f1e9b65e000) libk5crypto.so.3 => /lib64/libk5crypto.so.3 (0x00007f1e9b42d000) libcom_err.so.2 => /lib64/libcom_err.so.2 (0x00007f1e9b229000) libldap-2.4.so.2 => /lib64/libldap-2.4.so.2 (0x00007f1e9afd5000) liblber-2.4.so.2 => /lib64/liblber-2.4.so.2 (0x00007f1e9adc6000) /lib64/ld-linux-x86-64.so.2 (0x00007f1e9dc2b000) libunistring.so.0 => /lib64/libunistring.so.0 (0x00007f1e9aaae000) libdl.so.2 => /lib64/libdl.so.2 (0x00007f1e9a8aa000) libkrb5support.so.0 => /lib64/libkrb5support.so.0 (0x00007f1e9a69b000) libkeyutils.so.1 => /lib64/libkeyutils.so.1 (0x00007f1e9a497000) libresolv.so.2 => /lib64/libresolv.so.2 (0x00007f1e9a281000) libsasl2.so.3 => /lib64/libsasl2.so.3 (0x00007f1e9a064000) libssl3.so => /lib64/libssl3.so (0x00007f1e99e0e000) libsmime3.so => /lib64/libsmime3.so (0x00007f1e99be8000) libnss3.so => /lib64/libnss3.so (0x00007f1e998c6000) libnssutil3.so => /lib64/libnssutil3.so (0x00007f1e99697000) libplds4.so => /lib64/libplds4.so (0x00007f1e99493000) libplc4.so => /lib64/libplc4.so (0x00007f1e9928e000) libnspr4.so => /lib64/libnspr4.so (0x00007f1e99052000) libselinux.so.1 => /lib64/libselinux.so.1 (0x00007f1e98e2b000) libcrypt.so.1 => /lib64/libcrypt.so.1 (0x00007f1e98bf4000) librt.so.1 => /lib64/librt.so.1 (0x00007f1e989ec000) libpcre.so.1 => /lib64/libpcre.so.1 (0x00007f1e98788000)
後ほど比較するため、整形しておきます。
$ ldd ./bin/mediainfo | awk '{print $1;}' | grep -E "^lib" | sort libc.so.6 libcom_err.so.2 libcrypt.so.1 libcrypto.so.10 libcurl.so.4 libdl.so.2 libgcc_s.so.1 libgssapi_krb5.so.2 libidn2.so.0 libk5crypto.so.3 libkeyutils.so.1 libkrb5.so.3 libkrb5support.so.0 liblber-2.4.so.2 libldap-2.4.so.2 libm.so.6 libnghttp2.so.14 libnspr4.so libnss3.so libnssutil3.so libpcre.so.1 libplc4.so libplds4.so libpthread.so.0 libresolv.so.2 librt.so.1 libsasl2.so.3 libselinux.so.1 libsmime3.so libssh2.so.1 libssl.so.10 libssl3.so libstdc++.so.6 libunistring.so.0 libz.so.1
Lambda実行環境に存在している共有ライブラリ
続いてLambda実行環境で存在している共有ライブラリを確認します。以下のコードをLambda関数内で実行、CloudWatch Logsに出力される情報を確認していきます。
text = subprocess.check_output(["ls", "-l", "/usr/lib64"]) logger.info("lib64: {}".format(text))
なお、ここまでAmazon Linux 2上の操作では/lib64
を確認していましたが、このディレクトリ自体はシンボリックリンク、実体は/usr/lib64
でした。Lambda関数内ではこの/usr/lib64
ディレクトリを確認しています。
$ ls -l /lib64 lrwxrwxrwx 1 root root 9 3月 7 03:42 /lib64 -> usr/lib64
CloudWatch Logsで確認できる該当部分の出力、\nを改行に修正後、libで始まるファイルのみ抜粋すると以下となります。(awk '{print $9;}' | grep -E "^lib" | sort
というような処理を経ています。)
libBrokenLocale-2.26.so libBrokenLocale.so.1 libSegFault.so libacl.so.1 libacl.so.1.1.0 libanl-2.26.so libanl.so.1 libattr.so.1 libattr.so.1.1.0 libc-2.26.so libc.so.6 libcap.so.2 libcap.so.2.22 libcidn-2.26.so libcidn.so.1 libcom_err.so.2 libcom_err.so.2.1 libcrypto.so.1.0.2k libcrypto.so.10 libdl-2.26.so libdl.so.2 libffi.so.6 libffi.so.6.0.1 libform.so.6 libform.so.6.0 libformw.so.6 libformw.so.6.0 libfreebl3.chk libfreebl3.so libfreeblpriv3.chk libfreeblpriv3.so libgcc_s-7-20180712.so.1 libgcc_s.so.1 libgmp.so.10 libgmp.so.10.2.0 libgmpxx.so.4 libgmpxx.so.4.4.0 libgssapi_krb5.so.2 libgssapi_krb5.so.2.2 libgssrpc.so.4 libgssrpc.so.4.2 libicudata.so.50 libicudata.so.50.1.2 libicui18n.so.50 libicui18n.so.50.1.2 libicuio.so.50 libicuio.so.50.1.2 libicule.so.50 libicule.so.50.1.2 libiculx.so.50 libiculx.so.50.1.2 libicutest.so.50 libicutest.so.50.1.2 libicutu.so.50 libicutu.so.50.1.2 libicuuc.so.50 libicuuc.so.50.1.2 libk5crypto.so.3 libk5crypto.so.3.1 libkdb5.so.8 libkdb5.so.8.0 libkeyutils.so.1 libkeyutils.so.1.5 libkrad.so.0 libkrad.so.0.0 libkrb5.so.3 libkrb5.so.3.3 libkrb5support.so.0 libkrb5support.so.0.1 libm-2.26.so libm.so.6 libmemusage.so libmenu.so.6 libmenu.so.6.0 libmenuw.so.6 libmenuw.so.6.0 libmvec-2.26.so libmvec.so.1 libncurses.so.6 libncurses.so.6.0 libncursesw.so.6 libncursesw.so.6.0 libnsl-2.26.so libnsl.so.1 libnspr4.so libnss_compat-2.26.so libnss_compat.so.2 libnss_dns-2.26.so libnss_dns.so.2 libnss_files-2.26.so libnss_files.so.2 libnssckbi.so libnssutil3.so libp11-kit.so.0 libp11-kit.so.0.3.0 libpanel.so.6 libpanel.so.6.0 libpanelw.so.6 libpanelw.so.6.0 libpcprofile.so libpcre.so.1 libpcre.so.1.2.0 libpcre16.so.0 libpcre16.so.0.2.0 libpcre32.so.0 libpcre32.so.0.0.0 libpcrecpp.so.0 libpcrecpp.so.0.0.0 libpcreposix.so.0 libpcreposix.so.0.0.1 libplc4.so libplds4.so libpopt.so.0 libpopt.so.0.0.0 libpthread-2.26.so libpthread.so.0 libresolv-2.26.so libresolv.so.2 librt-2.26.so librt.so.1 libselinux.so.1 libsepol.so.1 libssl.so.1.0.2k libssl.so.10 libstdc++.so.6 libstdc++.so.6.0.24 libtasn1.so.6 libtasn1.so.6.5.3 libthread_db-1.0.so libthread_db.so.1 libtic.so.6 libtic.so.6.0 libtinfo.so.6 libtinfo.so.6.0 libutil-2.26.so libutil.so.1 libverto.so.1 libverto.so.1.0.0 libz.so.1 libz.so.1.2.7
不足している共有ライブラリとMediaInfo実行ファイルををまとめてLambda Layerに設定して動作確認
MediaInfo実行に必要な共有ライブラリと、Lambda実行環境に存在している共有ライブラリを比較して、前者にのみ存在し後者に含まれないものをリストアップします。MediaInfo実行に必要な共有ライブラリはmediainfo_library_list.txt
、Lambda実行環境に存在している共有ライブラリはlambda_library_list.txt
としてファイルにまとめておき、comm
コマンドで比較、前者にのみ存在し後者に含まれない共有ライブラリファイルを抜き出してみました。
$ comm -23 mediainfo_library_list.txt lambda_library_list.txt libcrypt.so.1 libcurl.so.4 libidn2.so.0 liblber-2.4.so.2 libldap-2.4.so.2 libnghttp2.so.14 libnss3.so libsasl2.so.3 libsmime3.so libssh2.so.1 libssl3.so libunistring.so.0
これらをすべてをMediaInfoをコンパイルしたAmazon Linux 2上の/lib64
ディレクトリからlib
ディレクトリにコピーします。先ほどのlibcurl.so.4
、libnghttp2.so.14
の手順と同様ですね。シンボリックリンクとなっているものはその実体のファイルもコピーします。(libcrypt.so.1
だけシンボリックリンクと実体ファイルの命名規則が他と異なっているので注意が必要でした。)そしてMediaInfo実行ファイルmediainfo
と一緒にzipファイルにします。
$ cp -pa /lib64/libcrypt.so.1 ./lib/ $ cp -pa /lib64/libcrypt-2.26.so ./lib/ $ cp -pa /lib64/libcurl.so.4* ./lib/ $ cp -pa /lib64/libidn2.so.0* ./lib/ $ cp -pa /lib64/liblber-2.4.so.2* ./lib/ $ cp -pa /lib64/libldap-2.4.so.2* ./lib/ $ cp -pa /lib64/libnghttp2.so.14* ./lib/ $ cp -pa /lib64/libnss3.so* ./lib/ $ cp -pa /lib64/libsasl2.so.3* ./lib/ $ cp -pa /lib64/libsmime3.so* ./lib/ $ cp -pa /lib64/libssh2.so.1* ./lib/ $ cp -pa /lib64/libssl3.so* ./lib/ $ cp -pa /lib64/libunistring.so.0* ./lib/ $ $ ls -l ./lib/ 合計 4572 -rwxr-xr-x 1 ec2-user ec2-user 41032 1月 17 06:44 libcrypt-2.26.so lrwxrwxrwx 1 ec2-user ec2-user 16 3月 7 03:42 libcrypt.so.1 -> libcrypt-2.26.so lrwxrwxrwx 1 ec2-user ec2-user 16 3月 7 03:42 libcurl.so.4 -> libcurl.so.4.5.0 -rwxr-xr-x 1 ec2-user ec2-user 553840 10月 3 23:37 libcurl.so.4.5.0 lrwxrwxrwx 1 ec2-user ec2-user 16 3月 7 03:42 libidn2.so.0 -> libidn2.so.0.3.7 -rwxr-xr-x 1 ec2-user ec2-user 324216 11月 21 18:20 libidn2.so.0.3.7 lrwxrwxrwx 1 ec2-user ec2-user 21 3月 7 03:42 liblber-2.4.so.2 -> liblber-2.4.so.2.10.7 -rwxr-xr-x 1 ec2-user ec2-user 61800 7月 27 2018 liblber-2.4.so.2.10.7 lrwxrwxrwx 1 ec2-user ec2-user 21 3月 7 03:42 libldap-2.4.so.2 -> libldap-2.4.so.2.10.7 -rwxr-xr-x 1 ec2-user ec2-user 348336 7月 27 2018 libldap-2.4.so.2.10.7 lrwxrwxrwx 1 ec2-user ec2-user 21 3月 7 03:42 libnghttp2.so.14 -> libnghttp2.so.14.18.0 -rwxr-xr-x 1 ec2-user ec2-user 158280 10月 1 19:24 libnghttp2.so.14.18.0 -rwxr-xr-x 1 ec2-user ec2-user 1204272 1月 9 18:58 libnss3.so lrwxrwxrwx 1 ec2-user ec2-user 17 3月 7 03:42 libsasl2.so.3 -> libsasl2.so.3.0.0 -rwxr-xr-x 1 ec2-user ec2-user 121240 7月 27 2018 libsasl2.so.3.0.0 -rwxr-xr-x 1 ec2-user ec2-user 160096 1月 9 18:58 libsmime3.so lrwxrwxrwx 1 ec2-user ec2-user 16 3月 7 03:42 libssh2.so.1 -> libssh2.so.1.0.1 -rwxr-xr-x 1 ec2-user ec2-user 169928 8月 29 2019 libssh2.so.1.0.1 -rwxr-xr-x 1 ec2-user ec2-user 358112 1月 9 18:58 libssl3.so lrwxrwxrwx 1 ec2-user ec2-user 21 3月 7 03:42 libunistring.so.0 -> libunistring.so.0.1.2 -rwxr-xr-x 1 ec2-user ec2-user 1146512 7月 31 2018 libunistring.so.0.1.2 $ $ zip mediainfo_v19.09_include_libraries.zip ./bin/* ./lib/* adding: bin/mediainfo (deflated 64%) adding: lib/libcrypt-2.26.so (deflated 53%) adding: lib/libcrypt.so.1 (deflated 53%) adding: lib/libcurl.so.4 (deflated 52%) adding: lib/libcurl.so.4.5.0 (deflated 52%) adding: lib/libidn2.so.0 (deflated 60%) adding: lib/libidn2.so.0.3.7 (deflated 60%) adding: lib/liblber-2.4.so.2 (deflated 55%) adding: lib/liblber-2.4.so.2.10.7 (deflated 55%) adding: lib/libldap-2.4.so.2 (deflated 55%) adding: lib/libldap-2.4.so.2.10.7 (deflated 55%) adding: lib/libnghttp2.so.14 (deflated 52%) adding: lib/libnghttp2.so.14.18.0 (deflated 52%) adding: lib/libnss3.so (deflated 60%) adding: lib/libsasl2.so.3 (deflated 52%) adding: lib/libsasl2.so.3.0.0 (deflated 52%) adding: lib/libsmime3.so (deflated 56%) adding: lib/libssh2.so.1 (deflated 55%) adding: lib/libssh2.so.1.0.1 (deflated 55%) adding: lib/libssl3.so (deflated 55%) adding: lib/libunistring.so.0 (deflated 62%) adding: lib/libunistring.so.0.1.2 (deflated 62%)
zipファイルを展開すると以下のような構成です。
$ unzip mediainfo_v19.09_include_libraries.zip Archive: mediainfo_v19.09_include_libraries.zip inflating: bin/mediainfo inflating: lib/libcrypt-2.26.so inflating: lib/libcrypt.so.1 inflating: lib/libcurl.so.4 inflating: lib/libcurl.so.4.5.0 inflating: lib/libidn2.so.0 inflating: lib/libidn2.so.0.3.7 inflating: lib/liblber-2.4.so.2 inflating: lib/liblber-2.4.so.2.10.7 inflating: lib/libldap-2.4.so.2 inflating: lib/libldap-2.4.so.2.10.7 inflating: lib/libnghttp2.so.14 inflating: lib/libnghttp2.so.14.18.0 inflating: lib/libnss3.so inflating: lib/libsasl2.so.3 inflating: lib/libsasl2.so.3.0.0 inflating: lib/libsmime3.so inflating: lib/libssh2.so.1 inflating: lib/libssh2.so.1.0.1 inflating: lib/libssl3.so inflating: lib/libunistring.so.0 inflating: lib/libunistring.so.0.1.2 $ $ ls -R .: bin lib mediainfo_v19.09_include_libraries.zip ./bin: mediainfo ./lib: libcrypt-2.26.so liblber-2.4.so.2.10.7 libsasl2.so.3.0.0 libcrypt.so.1 libldap-2.4.so.2 libsmime3.so libcurl.so.4 libldap-2.4.so.2.10.7 libssh2.so.1 libcurl.so.4.5.0 libnghttp2.so.14 libssh2.so.1.0.1 libidn2.so.0 libnghttp2.so.14.18.0 libssl3.so libidn2.so.0.3.7 libnss3.so libunistring.so.0 liblber-2.4.so.2 libsasl2.so.3 libunistring.so.0.1.2
このzipファイルで、改めてLayerの新規バージョンを作成します。
$ aws lambda publish-layer-version \ --layer-name MediaInfo-Python38 \ --description "MediaInfo v19.09 (Amazon Linux 2) and shared libraries" \ --zip-file fileb://mediainfo_v19.09_include_libraries.zip \ --compatible-runtimes python3.8
Layerのバージョン含めたARNは"arn:aws:lambda:ap-northeast-1:123456789012:layer:MediaInfo-Python38:4"
でした。この情報をもとにLambda関数で使用するよう設定します。
$ aws lambda update-function-configuration \ --function-name MediaInfo-on-Lambda-Python38 \ --layers arn:aws:lambda:ap-northeast-1:123456789012:layer:MediaInfo-Python38:4
このLayerにmediainfo実行ファイルと不足している共有ライブラリのファイルを含んだ状態で、改めて動作確認をしてみます。実際にS3に動画ファイルをアップロードしてLambda関数をキック、CloudWatch Logsから実行結果を確認してみます。今度はエラーなくMediaInfoが実行されました!ファイルサイズ、幅(Width)、高さ(Height)の情報がそれぞれ取得できていることが確認できます。
まとめ
動画やオーディオファイルの各種情報を取得できるツールMediaInfoをAWS LambdaのPython 3.8
ランタイムで動作させ、Lambda関数内で動画ファイルのメタ情報を取得してみました。Python 3.7
ランタイムの場合と異なり、Python 3.8
ランタイムの場合にはMediaInfoの実行ファイルに加えて、Lambda実行環境で不足している共有ライブラリファイルをLayerに含める必要がありました。
補足 Lambda関数で実行したコード
本エントリで動作確認をしたPtyhonのコードが下記になります。内容についてはPtyhon 3.7でMediaInfoを動作させた前回のエントリと代わりありませんので、詳細はそちらを参照ください。1点だけ、Lambda実行環境に存在している共有ライブラリ確認のためls -l /usr/lib64
の実行結果をログに書き出す処理を追記しています。
import json import logging import os import subprocess import urllib.parse import boto3 SIGNED_URL_EXPIRATION = 300 logger = logging.getLogger('boto3') logger.setLevel(logging.INFO) def get_signed_url(expires_in, bucket, obj): s3_cli = boto3.client("s3") presigned_url = s3_cli.generate_presigned_url( 'get_object', Params={'Bucket': bucket, 'Key': obj}, ExpiresIn=expires_in) return presigned_url s3 = boto3.client('s3') logger.info("Loading function") def lambda_handler(event, context): logger.info(json.dumps(event)) text = subprocess.check_output(["ls", "-l", "/usr/lib64"]) logger.info("lib64: {}".format(text)) for s3_record in event['Records']: try: logger.info("Working on new s3_record...") key = urllib.parse.unquote_plus(s3_record['s3']['object']['key'], encoding='utf-8') bucket = s3_record['s3']['bucket']['name'] logger.info("Bucket: {} \t Key: {}".format(bucket, key)) signed_url = get_signed_url(SIGNED_URL_EXPIRATION, bucket, key) logger.info("Signed URL: {}".format(signed_url)) # MediaInfoを実行 json_output = subprocess.check_output( ["mediainfo", "--full", "--output=JSON", signed_url]) logger.info("MediaInfo Output: {}".format(json_output)) # MediaInfoの実行結果からFileSize, Width, Heightを取り出す json_data = json.loads(json_output) tracks = json_data['media']['track'] video_info = {} for track in tracks: if track['@type'] == "General": if 'GeneralFileSize' not in video_info: video_info['GeneralFileSize'] = track.get('FileSize') else: logger.info('MediaInfo num of General > 1') elif track['@type'] == "Video": if 'VideoHeight' not in video_info: video_info['VideoHeight'] = track.get('Height') else: logger.info('MediaInfo number of Video > 1') if 'VideoWidth' not in video_info: video_info['VideoWidth'] = track.get('Width') else: logger.info('MediaInfo number of Video Width > 1') logger.info("video_info: {}".format(video_info)) # ファイル名とFileSize, Width, Heightをログ出力 input_file_name = key.rsplit('/', 1)[-1] logger.info("\n{} Infomation => FileSize: {} Byte, Width: {} pixel, Height: {} pixel".format( input_file_name, video_info['GeneralFileSize'], video_info['VideoWidth'], video_info['VideoHeight'])) except Exception as e: print(e) print('Error getting object {} from bucket {}. Make sure they exist and your bucket is in the same region as this function.'.format(key, bucket)) raise e
脚注
- 2020/02下旬ぐらいまではamzn2-ami-hvm-2.0.20190313-xvvv86_64-gp2 (https://console.aws.amazon.com/ec2/v2/home#Images:visibility=public-images;search=amzn2-ami-hvm-2.0.20190313-x86_64-gp2)とAMIの指定があったようです。 ↩